﻿using AngleSharp.Dom;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using NewLife.Caching;
using NewLife.Data;
using NewLife.Redis.Core;
using WomanMotion.WebApi.Identity.JWT;
using WomanMotion.WebApi.Infrastructure.UnifyResult;
using WomanMotion.WebApi.Model.Dtos.Authoritys;
using WomanMotion.WebApi.Model.Dtos.Role;
using WomanMotion.WebApi.Model.Entitys;
using WomanMotion.WebApi.Service.IService;
using static Dapper.SqlMapper;

namespace WomanMotion.WebApi.Api.Filters
{
    /// <summary>
    /// 授权过滤器
    /// </summary>
    public class AuthorizationFilter : IAuthorizationFilter
    {
        private readonly IAuthoritysService _authoritys;
        private readonly INewLifeRedis _redisService;
        private readonly IUsersService _users;
        private readonly IRoleService _role;
        private readonly IConfiguration _configuration;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="common"></param>
        public AuthorizationFilter(IAuthoritysService authoritys, INewLifeRedis redisService, IUsersService users, IRoleService role, IConfiguration configuration)
        {
            _authoritys = authoritys;
            _redisService = redisService;
            _users = users;
            _role = role;
            _configuration = configuration;
        }

        /// <summary>
        /// 权限控制处理
        /// </summary>
        /// <param name="context"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            //当控制器标注特效[AllowAnonymous] 则不需要进行权限控制
            var isall = context.ActionDescriptor.EndpointMetadata.OfType<AllowAnonymousAttribute>().Any();//IAllowAnonymous
            if (isall)
            {
                return;
            }
            //不是控制器里的方法 无需验证
            if (!context.ActionDescriptor.IsControllerAction())
            {
                return;
            }
            //获取请求的区域，控制器，action名称
            var area = context.RouteData.DataTokens["area"]?.ToString();
            var controller = context.RouteData.Values["controller"]?.ToString();//User
            var action = context.RouteData.Values["action"]?.ToString();//UserLogin

            //获取用户信息
            var claims = context.HttpContext.User.Claims;
            var userid = claims.Where(m => m.Type == "UserId").First().Value;
            var username = claims.Where(m => m.Type == "UserName").First().Value;
            var rolecode = claims.Where(m => m.Type == "RoleCode").First().Value;
            //判断用户token里面的角色信息是否和缓存及数据库一致，以防后台更新了用户的角色但是Token里面保存的还是老的角色信息
            var redisuserrolename = "user_rolecode_" + userid;
            var redisuserrolevalue = _redisService.Get<string>(redisuserrolename);
            //当取不到缓存信息
            if (redisuserrolevalue == null)
            {
                //数据库查询
                var uservm = _users.GetById(long.Parse(userid));
                if (uservm == null)
                {
                    context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    context.Result = new JsonResult(ResultOutput.NotOk("授权系统查询不到用户信息，请重新登录系统"));
                    return;
                }
                var rolevm = _role.Get(m => m.Id == uservm.Roleid);
                if (rolevm == null)
                {
                    context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    context.Result = new JsonResult(ResultOutput.NotOk("授权系统查询不到角色信息，请重新登录系统"));
                    return;
                }
                redisuserrolevalue = rolevm.Code;
                //加入缓存
                _redisService.Set(redisuserrolename, redisuserrolevalue);
                if (rolevm.Code != rolecode)//当缓存的角色和token的角色不匹配
                {
                    context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    context.Result = new JsonResult(ResultOutput.NotOk("该用户角色信息已更新，请重新登录系统"));
                    return;
                }
            }
            if (redisuserrolevalue != rolecode)//当缓存的角色和token的角色不匹配
            {
                context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                context.Result = new JsonResult(ResultOutput.NotOk("该用户角色信息已更新，请重新登录系统"));
                return;
            }
            //判断用户是否禁用
            //拼装权限缓存系统key
            var redisusername = "user_ifdisable_" + userid;
            var redisuservalue = _redisService.Get<string>(redisusername);
            if (redisuservalue == "0")
            {
                context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                context.Result = new JsonResult(ResultOutput.NotOk("该用户已禁用"));
                return;
            }
            //当缓存系统查询不到
            if (redisuservalue == null)
            {
                //数据库查询
                var entity = _users.GetById(long.Parse(userid));
                if (entity == null)
                {
                    context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    context.Result = new JsonResult(ResultOutput.NotOk("验证系统找不到该用户"));
                    return;
                }
                //加入缓存
                _redisService.Set("user_ifdisable_" + userid, entity.Ifdisable.ToString());
                if (entity.Ifdisable == 0)
                {
                    context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    context.Result = new JsonResult(ResultOutput.NotOk("该用户已禁用"));
                    return;
                }
            }
            //判断角色是否禁用
            var redisrolename = "role_ifdisable_" + rolecode;
            var redisrolevalue = _redisService.Get<string>(redisrolename);
            if (redisrolevalue == "0")
            {
                context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                context.Result = new JsonResult(ResultOutput.NotOk("该角色已禁用"));
                return;
            }
            //当缓存系统查询不到
            if (redisrolevalue == null)
            {
                //数据库查询
                var entity = _role.Get(m => m.Code == rolecode);
                if (entity == null)
                {
                    context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    context.Result = new JsonResult(ResultOutput.NotOk("验证系统找不到该角色"));
                    return;
                }
                _redisService.Set("role_ifdisable_" + rolecode, entity.Ifdisable.ToString());
                if (entity.Ifdisable == 0)
                {
                    context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    context.Result = new JsonResult(ResultOutput.NotOk("该角色已禁用"));
                    return;
                }
            }

            //判断角色权限
            //拼装权限缓存系统key
            var redisauthname = "auth_" + rolecode + "_" + controller + "_" + action;
            var redisautovalue = _redisService.Get<AuthoritysReids>(redisauthname);
            //当缓存系统获取不到值
            if (redisautovalue == null)
            {
                //数据库查询
                var authvm = _authoritys.GetAuthoritysFirst(m => m.RoleCode == rolecode && m.ControllerCode == controller && m.ActionsCode == action);
                if (authvm == null)
                {
                    context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    context.Result = new JsonResult(ResultOutput.NotOk("你没有权限访问该接口"));
                    return;
                }
                //当数据库查询到信息 加入缓存
                _redisService.Set("auth_" + authvm.RoleCode + "_" + authvm.ControllerCode + "_" + authvm.ActionsCode, new AuthoritysReids() { RoleCode = authvm.RoleCode, ControllerCode = authvm.ControllerCode, ActionsCode = authvm.ActionsCode });
            }
            return;
        }
    }

    /// <summary>
    /// 验证是否是控制器里面的方法
    /// </summary>
    public static class ActionDescriptorExtension
    {
        /// <summary>
        /// 验证是否是控制器里面的方法
        /// </summary>
        /// <param name="actionDescriptor"></param>
        /// <returns></returns>
        public static bool IsControllerAction(this ActionDescriptor actionDescriptor)
        {
            return actionDescriptor is ControllerActionDescriptor;
        }
    }
}